#include "Socket.h"

namespace BASE{
Socket::Socket(){
	socketHandle = -1;
	_connected = false;
}

Socket::~Socket(){
	close();
}

int Socket::write(const void * data, int len){
	if (-1 == socketHandle){
		return -1;
	}
	int res = ::write(socketHandle, data, len);
	return res;
}

int Socket::read(void *data, int len){
	if (-1 == socketHandle){
		return -1;
	}
	int res = ::read(socketHandle, data, len);
	return res;
}

bool Socket::isConnected(int millsecs){
	if (_connected)
		return true;
	struct pollfd fds;
	fds.fd = socketHandle;
	fds.events = POLLIN | POLLOUT;
	int ret = poll(&fds, 1, millsecs);
	if (-1 == ret){
		_connected = false;
		return _connected;
	}
	int soError = 0;
	socklen_t len = sizeof(int);
	if (getsockopt(socketHandle, SOL_SOCKET, SO_ERROR, &soError, &len) == -1){
		return false;
	}
	if (0 == soError){
		_connected = true;
		return true;
	}
	return false;

}

bool Socket::setAddress(const char * address, const int port){
	memset(&_address, 0, sizeof(_address));
	_address.sin_family = AF_INET;
	_address.sin_port = htons(port);
	bool rc = true;
	if (NULL == address || address[0] == '\0'){
		_address.sin_addr.s_addr = htonl(INADDR_ANY);
	}else{
		char c;
		const char * p = address;
		bool isIPAddr = true;
		while((c = (*p++)) != '\0'){
			if ((c != '.') && (!((c >= '0') && (c <= '9')))){
				isIPAddr = false;
				break;
			}
		}
		if (isIPAddr){
			_address.sin_addr.s_addr = inet_addr(address);
		}else{
			//not thread safe, lock in multithread
			struct hostent * myHostEnt = gethostbyname(address);
			if (NULL != myHostEnt){
				memcpy(&(_address.sin_addr),*(myHostEnt->h_addr_list), sizeof(struct in_addr));	
			}else{
				rc = false;
			}
		}
	}
	return rc;
}

bool Socket::checkSocketHandle(){
	if(-1 == socketHandle && (socketHandle = ::socket(AF_INET, SOCK_STREAM, 0)) == -1){
		return false;
	}
	return true;
}

bool Socket::connect(){
	if(!checkSocketHandle()){
		return false;
	}
	return (0 == ::connect(socketHandle, (struct sockaddr *)&_address, sizeof(_address)));
}

bool Socket::connectTimeout(int millsecs){
	int ret = connect();
	if (0 != ret){
		if (errno == EINPROGRESS){
			return isConnected(millsecs);		
		}
	}
	return ret;
}

void Socket::close(){
	if( -1 != socketHandle){
		::close(socketHandle);
		socketHandle = -1;
	}
}

void Socket::shutdown(){
	if (-1 != socketHandle){
		::shutdown(socketHandle, SHUT_WR);
		socketHandle = -1;
	}
}

bool Socket::setReuseAddress(bool on){
	bool rc = false;
	int value = on ? 1 : 0;
	if (checkSocketHandle()){
		rc = (setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, 
			(const void*)(&value), sizeof(value)) == 0);
	}
	return rc;
}

bool Socket::setSoLinger(bool doLinger, int sec){
	bool rc = false;
	struct linger lingerTime;
	lingerTime.l_onoff = doLinger ? 1 : 0;
	lingerTime.l_linger = sec;
	if (checkSocketHandle()){
		rc = (setsockopt(socketHandle, SOL_SOCKET, SO_LINGER,
			(const void *)(&lingerTime), sizeof(lingerTime)) == 0);
	}
	return rc;
}

bool Socket::setTcpNoDelay(bool noDelay){
	bool rc = false;
	int noDelayInt = noDelay ? 1 : 0;
	if (checkSocketHandle()){
		rc = (setsockopt(socketHandle, IPPROTO_TCP, TCP_NODELAY,
			(const void*)(&noDelayInt), sizeof(noDelayInt)) == 0);
	}
	return rc;
}

bool Socket::setBlocking(bool block){
	bool rc = false;
	if (checkSocketHandle()){
		int flags = fcntl(socketHandle, F_GETFL, NULL);
		if (flags >= 0){
			if (block){
				flags &= ~O_NONBLOCK;
			}else{
				flags |= O_NONBLOCK;
			}
			if (fcntl(socketHandle, F_SETFL, flags) >= 0){
				rc = true;
			}
		}
	}
	return rc;
}

void Socket::getIpAndPort(std::string & ip, uint16_t & port){
	char dest[32] = {0};
	unsigned long ad = ntohl(_address.sin_addr.s_addr);
	sprintf(dest, "%d.%d.%d.%d", static_cast<int>((ad >> 24) & 255),
		static_cast<int>((ad >> 16) & 255),
		static_cast<int>((ad >> 8) & 255),
		static_cast<int>(ad & 255));
	ip = ntohs(_address.sin_port);
}

uint64_t Socket::getId(){
	uint64_t ip = ntohs(_address.sin_port);
	ip <<= 32;
	ip |= _address.sin_addr.s_addr;
	return ip;
}
}
